// addrspace.cc 
//	Routines to manage address spaces (executing user programs).
//
//	In order to run a user program, you must:
//
//	1. link with the -n -T 0 option 
//	2. run coff2noff to convert the object file to Nachos format
//		(Nachos object code format is essentially just a simpler
//		version of the UNIX executable object code format)
//	3. load the NOFF file into the Nachos file system
//		(if you are using the "stub" file system, you
//		don't need to do this last step)
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "main.h"
#include "addrspace.h"
#include "machine.h"
#include "noff.h"

//----------------------------------------------------------------------
// SwapHeader
// 	Do little endian to big endian conversion on the bytes in the 
//	object file header, in case the file was generated on a little
//	endian machine, and we're now running on a big endian machine.
//----------------------------------------------------------------------

static void 
SwapHeader (NoffHeader *noffH)
{
    noffH->noffMagic = WordToHost(noffH->noffMagic);
    noffH->code.size = WordToHost(noffH->code.size);
    noffH->code.virtualAddr = WordToHost(noffH->code.virtualAddr);
    noffH->code.inFileAddr = WordToHost(noffH->code.inFileAddr);
#ifdef RDATA
    noffH->readonlyData.size = WordToHost(noffH->readonlyData.size);
    noffH->readonlyData.virtualAddr = 
           WordToHost(noffH->readonlyData.virtualAddr);
    noffH->readonlyData.inFileAddr = 
           WordToHost(noffH->readonlyData.inFileAddr);
#endif 
    noffH->initData.size = WordToHost(noffH->initData.size);
    noffH->initData.virtualAddr = WordToHost(noffH->initData.virtualAddr);
    noffH->initData.inFileAddr = WordToHost(noffH->initData.inFileAddr);
    noffH->uninitData.size = WordToHost(noffH->uninitData.size);
    noffH->uninitData.virtualAddr = WordToHost(noffH->uninitData.virtualAddr);
    noffH->uninitData.inFileAddr = WordToHost(noffH->uninitData.inFileAddr);

#ifdef RDATA
    DEBUG(dbgAddr, "code = " << noffH->code.size <<  
                   " readonly = " << noffH->readonlyData.size <<
                   " init = " << noffH->initData.size <<
                   " uninit = " << noffH->uninitData.size << "\n");
#endif
}

//----------------------------------------------------------------------
// AddrSpace::AddrSpace
// 	Create an address space to run a user program.
//	Set up the translation from program memory to physical 
//	memory.  For now, this is really simple (1:1), since we are
//	only uniprogramming, and we have a single unsegmented page table
//----------------------------------------------------------------------
/*
AddrSpace::AddrSpace()
{
    pageTable = new TranslationEntry[NumPhysPages];
    for (int i = 0; i < NumPhysPages; i++) {
	pageTable[i].virtualPage = i;	// for now, virt page # = phys page #
	pageTable[i].physicalPage = i;
	pageTable[i].valid = TRUE;
	pageTable[i].use = FALSE;
	pageTable[i].dirty = FALSE;
	pageTable[i].readOnly = FALSE;  
    }
    
    // zero out the entire address space
    bzero(kernel->machine->mainMemory, MemorySize);
}*/

//----------------------------------------------------------------------
// AddrSpace::AddrSpace
// 	Create an address space to run a user program.
//	Set up the translation from program memory to physical 
//	memory.  For now, this is really simple (1:1), since we are
//	only uniprogramming, and we have a single unsegmented page table
//----------------------------------------------------------------------

AddrSpace::AddrSpace()
{
    pageTable = new TranslationEntry[NumPhysPages];
/*
    for (int i = 0; i < NumPhysPages; i++) {
	pageTable[i].virtualPage = i;	// for now, virt page # = phys page #
	pageTable[i].physicalPage = i;
	pageTable[i].valid = TRUE;
	pageTable[i].use = FALSE;
	pageTable[i].dirty = FALSE;
	pageTable[i].readOnly = FALSE;  
    }*/
    
    // zero out the entire address space, to zero the unitialized data segment 
    // and the stack segment
//    bzero(kernel->machine->mainMemory, MemorySize);
}

//----------------------------------------------------------------------
// AddrSpace::~AddrSpace
// 	Dealloate an address space.
//----------------------------------------------------------------------

AddrSpace::~AddrSpace()
{
   delete pageTable;
//   delete memoryBmp;
}

// the original function
//----------------------------------------------------------------------
// AddrSpace::Load
// 	Load a user program into memory from a file.
//
//	Assumes that the page table has been initialized, and that
//	the object code file is in NOFF format.
//
//	"fileName" is the file containing the object code to load into memory
//----------------------------------------------------------------------
/*
bool 
AddrSpace::Load(char *fileName) 
{
    OpenFile *executable = kernel->fileSystem->Open(fileName);
    NoffHeader noffH;
    unsigned int size;

    if (executable == NULL) {
	cerr << "Unable to open file " << fileName << "\n";
	return FALSE;
    }

    executable->ReadAt((char *)&noffH, sizeof(noffH), 0);
    if ((noffH.noffMagic != NOFFMAGIC) && 
		(WordToHost(noffH.noffMagic) == NOFFMAGIC))
    	SwapHeader(&noffH);
    ASSERT(noffH.noffMagic == NOFFMAGIC);

#ifdef RDATA
// how big is address space?
    size = noffH.code.size + noffH.readonlyData.size + noffH.initData.size +
           noffH.uninitData.size + UserStackSize;	
                                                // we need to increase the size
						// to leave room for the stack
#else
// how big is address space?
    size = noffH.code.size + noffH.initData.size + noffH.uninitData.size 
			+ UserStackSize;	// we need to increase the size
						// to leave room for the stack
#endif
    numPages = divRoundUp(size, PageSize);
    size = numPages * PageSize;

    ASSERT(numPages <= NumPhysPages);		// check we're not trying
						// to run anything too big --
						// at least until we have
						// virtual memory

    DEBUG(dbgAddr, "Initializing address space: " << numPages << ", " << size);

// then, copy in the code and data segments into memory
// Note: this code assumes that virtual address = physical address
    if (noffH.code.size > 0) {
        DEBUG(dbgAddr, "Initializing code segment.");
	DEBUG(dbgAddr, noffH.code.virtualAddr << ", " << noffH.code.size);
        executable->ReadAt(
		&(kernel->machine->mainMemory[noffH.code.virtualAddr]), 
			noffH.code.size, noffH.code.inFileAddr);
    }
    if (noffH.initData.size > 0) {
        DEBUG(dbgAddr, "Initializing data segment.");
	DEBUG(dbgAddr, noffH.initData.virtualAddr << ", " << noffH.initData.size);
        executable->ReadAt(
		&(kernel->machine->mainMemory[noffH.initData.virtualAddr]),
			noffH.initData.size, noffH.initData.inFileAddr);
    }

#ifdef RDATA
    if (noffH.readonlyData.size > 0) {
        DEBUG(dbgAddr, "Initializing read only data segment.");
	DEBUG(dbgAddr, noffH.readonlyData.virtualAddr << ", " << noffH.readonlyData.size);
        executable->ReadAt(
		&(kernel->machine->mainMemory[noffH.readonlyData.virtualAddr]),
			noffH.readonlyData.size, noffH.readonlyData.inFileAddr);
    }
#endif
	
	Print();	

    delete executable;			// close file
    return TRUE;			// success
}
*/

// version 1
/*
bool 
AddrSpace::Load(char *fileName) 
{
	OpenFile *executable = kernel->fileSystem->Open(fileName);
    NoffHeader noffH;
    unsigned int i, size;

	if (executable == NULL) 
	{
		cerr << "Unable to open file " << fileName << "\n";
		return FALSE;
    }
	
    executable->ReadAt((char *)&noffH, sizeof(noffH), 0);

	//check NOFFMAGIC
    if ((noffH.noffMagic != NOFFMAGIC) && 
		(WordToHost(noffH.noffMagic) == NOFFMAGIC))
    	SwapHeader(&noffH);
    ASSERT(noffH.noffMagic == NOFFMAGIC);

//    size = divRoundUp(noffH.code.size,PageSize) * PageSize + 
//    	   divRoundUp(noffH.initData.size, PageSize) * PageSize + 
//    	   noffH.uninitData.size + UserStackSize;
                                      //Because the bitmap ask for reading in PageSizes,
                                      //the code and data segment must set int chuncks.

#ifdef RDATA
// how big is address space?
    size = divRoundUp(noffH.code.size,PageSize) * PageSize + 
		   divRoundUp(noffH.readonlyData.size,PageSize) * PageSize + 
		   divRoundUp(noffH.initData.size,PageSize) * PageSize +
           noffH.uninitData.size + UserStackSize;	
                                                // we need to increase the size
						// to leave room for the stack
#else
// how big is address space?
    size = divRoundUp(noffH.code.size,PageSize) * PageSize + 
		   divRoundUp(noffH.initData.size,PageSize) * PageSize +
           noffH.uninitData.size + UserStackSize;		// we need to increase the size
						// to leave room for the stack
#endif

    numPages = divRoundUp(size, PageSize);
	size = numPages * PageSize;

//	memoryBmp = new Bitmap(NumPhysPages);
//  cout << "numPages is " << numPages << endl;
//  cout << "memoryBmp->NumClear() is " << kernel->memoryBmp->NumClear() << endl;
    ASSERT(numPages <= kernel->memoryBmp->NumClear());     // check we're not trying
                                                   // to run anything too big 
												   // at least until we have virtual memory

//  first, set up the translation 
//	pageTable = new TranslationEntry[numPages];
    for (i = 0; i < numPages; i++) 
    {
        pageTable[i].virtualPage = i;         // for now, virtual page # = phys page #
        pageTable[i].physicalPage = kernel->memoryBmp->FindAndSet();
		//cout << "physicalPage = " << pageTable[i].physicalPage << endl;
        pageTable[i].valid = TRUE;
        pageTable[i].use = FALSE;
        pageTable[i].dirty = FALSE;
        pageTable[i].readOnly = FALSE; // if the code segment was entirely on 
                                       // a separate page, we could set its 
                                       // pages to be read-only
    }
    
	// then, copy in the code and data segments into memory

	//Read the file content in the physical memory to mainMemory
	//First read in the code,
	//then read in the data
    if (noffH.code.size > 0) 
    {
//        printf("Initializing code segment, at 0x%x, size %d\n", 
//                            noffH.code.virtualAddr, noffH.code.size);

        unsigned int numP;  //firstOff;
        numP = divRoundUp(noffH.code.size, PageSize);
//		cout << "num of code = " << numP << endl;
        for (i = 0; i < numP; i++)
        {
//            printf("Address:%d\n", pageTable[i].physicalPage * PageSize);
//            printf("virtualAddr:%d\n", noffH.code.virtualAddr + i * PageSize);
//            printf("In File:%d\n", noffH.code.inFileAddr + i * PageSize);
//            printf("copy size %d\n\n",PageSize);
            executable->ReadAt(&(kernel->machine->mainMemory[pageTable[i].physicalPage * PageSize]), 
            				   PageSize, 
            				   noffH.code.inFileAddr + i * PageSize);
        }
	}

    if (noffH.initData.size > 0)
    {
//		printf("Initializing data segment, at 0x%x, size %d\n", 
//		               noffH.initData.virtualAddr, noffH.initData.size);

		unsigned int numP,firstP;
        numP = divRoundUp(noffH.initData.size, PageSize);
        firstP = divRoundUp(noffH.initData.virtualAddr, PageSize);
 
        for (i = 0; i < numP; i++)
        {
 //           printf("Address:%d\n",pageTable[i].physicalPage*PageSize);
 //           printf("In File:%d\n",noffH.initData.inFileAddr+i*PageSize);
 //           printf("copy size %d\n\n");
            executable->ReadAt(&(kernel->machine->mainMemory[pageTable[i + firstP].physicalPage * PageSize]),
            				   PageSize, 
            				   noffH.initData.inFileAddr + i * PageSize);
        }
	}

#ifdef RDATA
    if (noffH.readonlyData.size > 0) 
	{
//		printf("Initializing rdata segment, at 0x%x, size %d\n", 
//		               noffH.readonlyData.virtualAddr, noffH.readonlyData.size);
        DEBUG(dbgAddr, "Initializing read only data segment.");
		DEBUG(dbgAddr, noffH.readonlyData.virtualAddr << ", " << noffH.readonlyData.size);
		unsigned int numP,firstP;
        numP = divRoundUp(noffH.readonlyData.size, PageSize);
        firstP = divRoundUp(noffH.readonlyData.virtualAddr, PageSize);

//		cout << "firstP of rdata = " << firstP << endl;
//		cout << "numP of rdata = " << numP << endl;
 
        for (i = 0; i < numP; i++)
        {
//            printf("Address:%d\n", pageTable[i].physicalPage * PageSize);
//			printf("virtualAddr:%d\n", noffH.readonlyData.virtualAddr + i * PageSize);
//            printf("In File:%d\n", noffH.readonlyData.inFileAddr + i * PageSize);
//            printf("copy size %d\n\n",PageSize);
        	executable->ReadAt(&(kernel->machine->mainMemory[pageTable[i + firstP].physicalPage * PageSize]), 
							   PageSize, 
							   noffH.readonlyData.inFileAddr + i * PageSize);
    	}
	}
#endif
    
//    Print();
	delete executable;	    // close file
    return TRUE;			// success
}
*/

// version 2
bool 
AddrSpace::Load(char *fileName) 
{
	OpenFile *executable = kernel->fileSystem->Open(fileName);
	NoffHeader noffH;
	unsigned int i, size,unused;
	char *buffer;
	int current;	//current available page number

	executable->ReadAt((char *)&noffH, sizeof(noffH), 0);

	if ((noffH.noffMagic != NOFFMAGIC) &&
		(WordToHost(noffH.noffMagic) == NOFFMAGIC))
		SwapHeader(&noffH);

	ASSERT(noffH.noffMagic == NOFFMAGIC);

	// how big is address space?
	size = noffH.code.size + noffH.initData.size + noffH.uninitData.size + UserStackSize;	// we need to increase the size
																			// to leave room for the stack
	numPages = divRoundUp(size, PageSize);
//	printf("the user program page table size is %d\n",numPages);
	size = numPages * PageSize;
	ASSERT(numPages <= kernel->memoryBmp->NumClear());  // check we're not trying
						// to run anything too big --
						// at least until we have
						// virtual memory

	buffer = new char[size];
	if (noffH.code.size > 0)
	{
		    DEBUG(dbgAddr, "Initializing code segment.");
			DEBUG(dbgAddr, noffH.code.virtualAddr << ", " << noffH.code.size);
		    executable->ReadAt(
			&(buffer[noffH.code.virtualAddr]),
				noffH.code.size, noffH.code.inFileAddr);
	}
	if (noffH.initData.size > 0)
	{
		DEBUG(dbgAddr, "Initializing data segment.");
		DEBUG(dbgAddr, noffH.initData.virtualAddr << ", " << noffH.initData.size);
		executable->ReadAt(
		&(buffer[noffH.initData.virtualAddr]),
			noffH.initData.size, noffH.initData.inFileAddr);
	}

	#ifdef RDATA
		if (noffH.readonlyData.size > 0)
		{
			DEBUG(dbgAddr, "Initializing read only data segment.");
			DEBUG(dbgAddr, noffH.readonlyData.virtualAddr << ", " << noffH.readonlyData.size);
			executable->ReadAt(
			&(buffer[noffH.readonlyData.virtualAddr]),
				noffH.readonlyData.size, noffH.readonlyData.inFileAddr);
		}
	#endif


	// first, set up the translation
//	pageTable = new TranslationEntry[numPages];
	for (i = 0; i < numPages; i++)
	{
		unused = kernel->memoryBmp->FindAndSet();

		memcpy(&(kernel->machine->mainMemory[unused*PageSize]),
			   &(buffer[i*PageSize]),
			   PageSize);

		pageTable[i].virtualPage = i;	// for now, virtual page # = phys page #
		pageTable[i].physicalPage = unused;
		pageTable[i].valid = TRUE;
		pageTable[i].use = FALSE;
		pageTable[i].dirty = FALSE;
		pageTable[i].readOnly = FALSE;  // if the code segment was entirely on
						// a separate page, we could set its
						// pages to be read-only
	}
	// zero out the entire address space, to zero the unitialized data segment
	// and the stack segment
//	bzero(kernel->machine->mainMemory, MemorySize);

//	Print();
	delete executable;	    // close file
    return TRUE;			// success
}


//----------------------------------------------------------------------
// AddrSpace::Execute
// 	Run a user program using the current thread
//
//      The program is assumed to have already been loaded into
//      the address space
//
//----------------------------------------------------------------------

void 
AddrSpace::Execute() 
{

    kernel->currentThread->space = this;

    this->InitRegisters();		// set the initial register values
    this->RestoreState();		// load page table register

	kernel->spaceList->Prepend(this);

    kernel->machine->Run();		// jump to the user progam

    ASSERTNOTREACHED();			// machine->Run never returns;
					// the address space exits
					// by doing the syscall "exit"
}


//----------------------------------------------------------------------
// AddrSpace::InitRegisters
// 	Set the initial values for the user-level register set.
//
// 	We write these directly into the "machine" registers, so
//	that we can immediately jump to user code.  Note that these
//	will be saved/restored into the currentThread->userRegisters
//	when this thread is context switched out.
//----------------------------------------------------------------------

void
AddrSpace::InitRegisters()
{
    Machine *machine = kernel->machine;
    int i;

    for (i = 0; i < NumTotalRegs; i++)
	machine->WriteRegister(i, 0);

    // Initial program counter -- must be location of "Start", which
    //  is assumed to be virtual address zero
    machine->WriteRegister(PCReg, 0);	

    // Need to also tell MIPS where next instruction is, because
    // of branch delay possibility
    // Since instructions occupy four bytes each, the next instruction
    // after start will be at virtual address four.
    machine->WriteRegister(NextPCReg, 4);

   // Set the stack register to the end of the address space, where we
   // allocated the stack; but subtract off a bit, to make sure we don't
   // accidentally reference off the end!
    machine->WriteRegister(StackReg, numPages * PageSize - 16);
    DEBUG(dbgAddr, "Initializing stack pointer: " << numPages * PageSize - 16);
}

//----------------------------------------------------------------------
// AddrSpace::SaveState
// 	On a context switch, save any machine state, specific
//	to this address space, that needs saving.
//
//	For now, don't need to save anything!
//----------------------------------------------------------------------

void AddrSpace::SaveState() 
{
}

//----------------------------------------------------------------------
// AddrSpace::SaveReg
// 	On a context switch, save any machine state, specific
//	to the machine registers.
//----------------------------------------------------------------------

void AddrSpace::SaveReg()
{
	for (int i = 0; i < NumTotalRegs; i++)
	{
		registerTable[i] = kernel->machine->ReadRegister(i);
//		cout << i << ": " << registerTable[i] << endl;
	}
}

//----------------------------------------------------------------------
// AddrSpace::RestoreReg
// 	On a context switch, restore any machine state, specific
//	to the machine registers.
//----------------------------------------------------------------------

void AddrSpace::RestoreReg() 
{
	for (int i = 0; i < NumTotalRegs; i++)
	{
		kernel->machine->WriteRegister(i, this->registerTable[i]);
//		cout << i << ": " << registerTable[i] << endl;
	}
}

//----------------------------------------------------------------------
// AddrSpace::RestoreState
// 	On a context switch, restore the machine state so that
//	this address space can run.
//
//      For now, tell the machine where to find the page table.
//----------------------------------------------------------------------

void AddrSpace::RestoreState() 
{
    kernel->machine->pageTable = pageTable;
    kernel->machine->pageTableSize = numPages;
}


//----------------------------------------------------------------------
// AddrSpace::Translate
//  Translate the virtual address in _vaddr_ to a physical address
//  and store the physical address in _paddr_.
//  The flag _isReadWrite_ is false (0) for read-only access; true (1)
//  for read-write access.
//  Return any exceptions caused by the address translation.
//----------------------------------------------------------------------
ExceptionType
AddrSpace::Translate(unsigned int vaddr, unsigned int *paddr, int isReadWrite)
{
    TranslationEntry *pte;
    int               pfn;
    unsigned int      vpn    = vaddr / PageSize;
    unsigned int      offset = vaddr % PageSize;

    if(vpn >= numPages) {
        return AddressErrorException;
    }

    pte = &pageTable[vpn];

    if(isReadWrite && pte->readOnly) {
        return ReadOnlyException;
    }

    pfn = pte->physicalPage;

    // if the pageFrame is too big, there is something really wrong!
    // An invalid translation was loaded into the page table or TLB.
    if (pfn >= NumPhysPages) {
        DEBUG(dbgAddr, "Illegal physical page " << pfn);
        return BusErrorException;
    }

    pte->use = TRUE;          // set the use, dirty bits

    if(isReadWrite)
        pte->dirty = TRUE;

    *paddr = pfn*PageSize + offset;

    ASSERT((*paddr < MemorySize));

    //cerr << " -- AddrSpace::Translate(): vaddr: " << vaddr <<
    //  ", paddr: " << *paddr << "\n";

    return NoException;
}

//----------------------------------------------------------------------
// AddrSpace::Print
// 	Print the contents of the bitmap, for debugging.
//
//	Could be done in a number of ways, but we just print the #'s of
//	all the bits that are set in the bitmap.
//----------------------------------------------------------------------

void
AddrSpace::Print()
{
    cout << "============  AddrSpace =============" << endl; \
    cout << "     virtualPage    physicalPage" << endl;
    for (int i = 0; i < numPages; i++) {
	  cout << "          " << pageTable[i].virtualPage << "             " << pageTable[i].physicalPage << endl;
    }
    cout << "=====================================" << endl <<endl;
}




